package cn.edu.njust.aio;

import cn.edu.njust.EchoServer;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.locks.LockSupport;

public class AioEchoServer implements EchoServer {
    private AsynchronousServerSocketChannel serverChannel;

    @Override
    public void listen(int port) {
        try {
            serverChannel = AsynchronousServerSocketChannel.open();
            serverChannel.bind(new InetSocketAddress(port));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void serve() {
        serverChannel.accept(serverChannel, new AcceptCompletionHandler());
        // 使用park阻塞住，否则就直接方法结束，退出了
        LockSupport.park();
    }
}

/**
 * 回调类，用于处理客户端连接完成事件
 */
@Slf4j
class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel> {

    @Override
    public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) {
        try {
            log.info("接收客户端{}的连接请求", result.getRemoteAddress());
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 继续异步接收请求！！！这一步很重要
        attachment.accept(attachment, this);

        // 异步读数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        result.read(buffer, buffer, new ReadCompletionHandler(result));
    }

    @Override
    public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) {
        log.error("连接客户端失败");
    }
}

/**
 * 回调类，用于处理从客户端读数据结束的事件
 */
@Slf4j
class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {

    private final AsynchronousSocketChannel clientChannel;

    public ReadCompletionHandler(AsynchronousSocketChannel clientChannel) {
        this.clientChannel = clientChannel;
    }

    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        // flip一下，方便后面读
        attachment.flip();
        log.info("从客户端接收到消息{}", new String(attachment.array(), 0, attachment.limit()));
        clientChannel.write(attachment, attachment, new WriteCompletionHandler(clientChannel));
    }

    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        log.error("读取客户端数据失败");
    }
}

/**
 * 回调类，用于处理向客户端写数据结束的事件
 */
@Slf4j
class WriteCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {

    private final AsynchronousSocketChannel clientChannel;

    public WriteCompletionHandler(AsynchronousSocketChannel clientChannel) {
        this.clientChannel = clientChannel;
    }

    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        log.info("向客户端发送数据{}成功", new String(attachment.array(), 0, attachment.limit()));
        try {
            // 发完消息记得关闭输出，否则会导致客户端read一直阻塞！(这里不采用拆包机制)
            clientChannel.shutdownOutput();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        log.error("向客户端发送数据失败");
    }
}